package com.nbjtjc.safe.activiti.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.nbjtjc.safe.activiti.listener.DangerFlowListener;
import com.nbjtjc.safe.activiti.mapper.ExtActNodeMapper;
import com.nbjtjc.safe.activiti.model.*;
import com.nbjtjc.safe.bean.MyCache;
import com.nbjtjc.safe.bean.Page;
import com.nbjtjc.safe.bean.Result;
import com.nbjtjc.safe.mapper.danger.DangerInspectListPublicMapper;
import com.nbjtjc.safe.mapper.danger.DangerInspectPublicMapper;
import com.nbjtjc.safe.model.danger.DangerInspectPublic;
import com.nbjtjc.safe.service.danger.DangerInspectPublicService;
import com.nbjtjc.safe.util.ActUtils;
import com.nbjtjc.safe.util.MyShiroUtils;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @auther guqianbin
 * @date 2018/6/7 11:16
 * 隐患工作流的相关类
 **/
@Service
public class DangerFlowService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private TaskService taskService;
    @Autowired
    IdentityService identityservice;
    @Autowired
    RuntimeService runtimeservice;

    @Autowired
    HistoryService historyService;
    @Autowired
    private ActUtils actUtils;
    @Autowired
    private DangerInspectPublicService dangerInspectPublicService;
    @Autowired
    private MyCache myCache;

    @Autowired
    private ExtActNodeMapper extActNodeMapper;

    @Autowired
    private DangerInspectPublicMapper dangerInspectPublicMapper;
    @Autowired
    private DangerFlowListener dangerFlowListener;

    @Autowired
    private DangerInspectListPublicMapper dangerInspectListPublicMapper;
    @Autowired
    private ExtActTaskReassignService extActTaskReassignService;

    /**
     * 开始流程
     * @param dangerInspectPublic
     * @return
     */
    @Transactional
    public Result startDangerFlow(DangerInspectPublic dangerInspectPublic) {
        Result result = new Result();
        ActivitiParams activitiParams = dangerInspectPublic.getActivitiParams();
        dangerInspectPublic.setType(activitiParams.getType());
        dangerInspectPublic.setPublicCompanyId(MyShiroUtils.getCompanyId());
        result = dangerInspectPublicService.add(dangerInspectPublic);
        if (!result.isSuccess()) {
            return result;
        }
        Map<String, Object> variables = activitiParams.getParamsMap();
        variables.put("busKey", dangerInspectPublic.getId());
        variables.put("dangerFlowListener", dangerFlowListener);
        identityservice.setAuthenticatedUserId(MyShiroUtils.getSessionUserId().toString());
        ProcessInstance processInstance = runtimeservice
                .startProcessInstanceByKey("dangerProcess"
                        , dangerInspectPublic.getId().toString(), variables);
        dangerInspectPublic.setProcessInstanceId(processInstance.getId());
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        taskService.setOwner(task.getId(), MyShiroUtils.getSessionUserId().toString());
        dangerInspectPublicService.update(dangerInspectPublic);
        if (ObjectUtil.isNotNull(processInstance)) {
            RunningProcess runningProcess = new RunningProcess();
            runningProcess.setActivityid(processInstance.getActivityId());
            runningProcess.setBusinesskey(processInstance.getBusinessKey());
            runningProcess.setExecutionid(processInstance.getId());
            runningProcess.setProcessInstanceid(processInstance.getProcessInstanceId());
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
            if(CollectionUtil.isNotEmpty(tasks)){
                List<MyTask> myTasks=new ArrayList<>();
                tasks.forEach(task1 -> {
                    MyTask myTask=new MyTask();
                    myTask.setId(task1.getId());
                    myTasks.add(myTask);
                });
                runningProcess.setMyTasks(myTasks);
            }
            result.setData(runningProcess);
        } else {
            result.setFalse("无任务");
        }
        return result;
    }

    /**
     * 做任务
     * @param activitiParams
     * @return
     */
    public Result doTask(ActivitiParams activitiParams) {
        long start = System.currentTimeMillis();
        Result result = new Result();
        String taskId = activitiParams.getTaskId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Map<String, Object> params = activitiParams.getParamsMap();
        if (params.containsKey("comment")) {
            taskService.addComment(taskId, task.getProcessInstanceId(), params.get("comment").toString());
        }
        if (!params.containsKey("applyUserId")) {
            ProcessInstance processInstance = runtimeservice.createProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId()).singleResult();
            params.put("applyUserId", processInstance.getStartUserId());
        }
        taskService.setVariables(taskId, params);
        taskService.complete(taskId, params);

        return result;
    }

    /**
     * 获取未执行的任务
     * @param page
     * @param taskDefineKey
     * @return
     */
    public Result getUndoTask(Page page,String taskDefineKey) {
        Result result = new Result();
        Map<String, Object> data = new HashMap<>();
        List<MyTask> myTaskList = new ArrayList<>();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("dangerProcess").latestVersion().singleResult();
        page.countPage(taskService.createTaskQuery()
                .taskAssignee(MyShiroUtils.getSessionUserId().toString())
                .processDefinitionKey(processDefinition.getKey()).count());
        List<Task> taskList=new ArrayList<>();
        if(StringUtils.isNotEmpty(taskDefineKey)){
            taskList = taskService.createTaskQuery()
                    .taskAssignee(MyShiroUtils.getSessionUserId().toString())
                    .processDefinitionKey(processDefinition.getKey())
                    .taskDefinitionKey(taskDefineKey)//查找指定类型的任务
                    .listPage(page.getPageSize() * (page.getPageCurrent() - 1), page.getPageSize());
        }else{
            taskList = taskService.createTaskQuery()
                    .taskAssignee(MyShiroUtils.getSessionUserId().toString())
                    .processDefinitionKey(processDefinition.getKey())
                    .listPage(page.getPageSize() * (page.getPageCurrent() - 1), page.getPageSize());
        }

        if (CollectionUtil.isNotEmpty(taskList)) {
            taskList.forEach(task -> {
                task.getExecutionId();
                Map<String, Object> params = runtimeservice.getVariables(task.getExecutionId());
                MyTask myTask = new MyTask();
                if (params.containsKey("userId")) {
                    myTask.setSubProcessStartUserId(params.get("userId").toString());
                }
                myTask.setCreateTime(task.getCreateTime());
                myTask.setProcessInstanceId(task.getProcessInstanceId());
                myTask.setName(task.getName());
                myTask.setId(task.getId());
                myTask.setTaskDefinitionKey(task.getTaskDefinitionKey());
                myTask.setExecutionId(task.getExecutionId());
                myTask.setUserId(task.getAssignee());
                myTask.setUserName(myCache.getUserName(task.getAssignee()));
                if (ObjectUtil.isNotNull(task.getAssignee())) {
                    myTask.setUserName(myCache.getUserName(task.getAssignee()));
                }
                myTaskList.add(myTask);
            });
        }
        compareTask(myTaskList, false);
        //todo 设置dangerInspectPublic
        data.put("data", myTaskList);
        data.put("page", page);
        result.setData(data);
        return result;
    }


    /**
     * 获取已经做的任务
     * @param page
     * @return
     */
    public Result getMyDoneTask(Page page) {
        Result result = Result.res();
        Map<String, Object> data = new HashMap<>();
        page.countPage(historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(MyShiroUtils.getSessionUserId().toString())
                .processDefinitionKey("dangerProcess")
                .count());

        List<HistoricTaskInstance> historicTaskInstanceList = historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(MyShiroUtils.getSessionUserId() + "")
                .processDefinitionKey("dangerProcess")
                .listPage(page.getPageSize() * (page.getPageCurrent() - 1), page.getPageSize());
        List<MyTask> myTaskList = new ArrayList<>();
        List<MyTask> historicTasks=new ArrayList<>();
        Set<String> runningprocessInstnaceIdSet = new HashSet<>();
        Set<String> historicProcessInstanceIdSet = new HashSet<>();
        List<Integer> dangerPublicIds = new ArrayList<>();
        Map<String, DangerProcess> dangerProcessMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(historicTaskInstanceList)) {

            Set<String> processInstanceIdSet = historicTaskInstanceList.stream()
                    .map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet());
            //查找正在运行的流程
            List<ProcessInstance> processInstances = runtimeservice.createProcessInstanceQuery()
                    .processInstanceIds(processInstanceIdSet).list();
            if (CollectionUtil.isNotEmpty(processInstances)) {
                runningprocessInstnaceIdSet.addAll(processInstances.stream().map(ProcessInstance::getProcessInstanceId)
                        .collect(Collectors.toSet()));
                List<String> dangerIdStr = processInstances.stream()
                        .map(ProcessInstance::getBusinessKey).collect(Collectors.toList());
                List<Integer> runningInspectIds = new ArrayList<>();
                dangerIdStr.forEach(idStr -> {
                    dangerPublicIds.add(Integer.parseInt(idStr));
                    runningInspectIds.add(Integer.parseInt(idStr));
                });
                DangerInspectPublic dangerInspectPublic = new DangerInspectPublic();
                dangerInspectPublic.setFinish(false);
                List<DangerInspectPublic> dangerInspectPublics = dangerInspectPublicMapper
                        .searchSimple(dangerInspectPublic, runningInspectIds);
                List<DangerProcess> dangerProcesses = compareRunningProcess(dangerInspectPublics);
                dangerProcessMap.putAll(dangerProcesses.stream()
                        .collect(Collectors.toMap(DangerProcess::getProcessInstanceid, dangerProcess -> dangerProcess)));
            }
            //查找历史流程
            List<HistoricProcessInstance> historicProcessInstances = historyService
                    .createHistoricProcessInstanceQuery().processInstanceIds(processInstanceIdSet).finished().list();
            if (CollectionUtil.isNotEmpty(historicProcessInstances)) {
                historicProcessInstanceIdSet.addAll(historicProcessInstances
                        .stream().map(HistoricProcessInstance::getId).collect(Collectors.toSet()));
                List<String> dangerIdStr = historicProcessInstances.stream()
                        .map(HistoricProcessInstance::getBusinessKey).collect(Collectors.toList());
                dangerIdStr.forEach(idStr -> {
                    dangerPublicIds.add(Integer.parseInt(idStr));
                });
            }

            historicTaskInstanceList.forEach(historicTaskInstance -> {
                MyTask myTask = new MyTask();
                myTask.setName(historicTaskInstance.getName());
                myTask.setProcessInstanceId(historicTaskInstance.getProcessInstanceId());
                myTask.setCreateTime(historicTaskInstance.getCreateTime());
                myTask.setEndTime(historicTaskInstance.getEndTime());
                myTask.setId(historicTaskInstance.getId());
                myTask.setUserId(historicTaskInstance.getAssignee());
                myTask.setProcessDefId(historicTaskInstance.getProcessDefinitionId());
                myTask.setTaskDefinitionKey(historicTaskInstance.getTaskDefinitionKey());
                List<Comment> comments = taskService.getTaskComments(historicTaskInstance.getId());
                if (CollectionUtil.isNotEmpty(comments)) {
                    myTask.setComment(comments.get(0).getFullMessage());
                }
                if (ObjectUtil.isNotNull(historicTaskInstance.getAssignee())) {
                    myTask.setUserName(myCache.getUserName(historicTaskInstance.getAssignee()));
                }
                if (historicProcessInstanceIdSet.contains(historicTaskInstance.getProcessInstanceId())) {
                    myTask.setProcessFinish(true);
                    historicTasks.add(myTask);
                } else {
                    myTask.setProcessFinish(false);
                    myTask.setDangerProcess(dangerProcessMap.get(myTask.getProcessInstanceId()));
                    myTaskList.add(myTask);
                }
            });
        }
        compareTask(historicTasks,true);
        myTaskList.addAll(historicTasks);
        data.put("data", myTaskList);
        data.put("page", page);
        result.setData(data);
        return result;
    }

    public Result getDangerHistoryTask(Page page) {

        Result result = Result.res();
        Map<String, Object> data = new HashMap<>();
        page.countPage(historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(MyShiroUtils.getSessionUserId().toString())
                .processDefinitionKey("dangerProcess")
                .count());
        List<HistoricTaskInstance> historicTaskInstanceList = historyService
                .createHistoricTaskInstanceQuery()
                .taskAssignee(MyShiroUtils.getSessionUserId() + "")
                .processDefinitionKey("dangerProcess")
                .listPage(page.getPageSize() * (page.getPageCurrent() - 1), page.getPageSize());
        List<MyTask> myTaskList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(historicTaskInstanceList)) {
            historicTaskInstanceList.forEach(historicTaskInstance -> {
                MyTask myTask = new MyTask();
                myTask.setName(historicTaskInstance.getName());
                myTask.setProcessInstanceId(historicTaskInstance.getProcessInstanceId());
                myTask.setCreateTime(historicTaskInstance.getCreateTime());
                myTask.setEndTime(historicTaskInstance.getEndTime());
                myTask.setId(historicTaskInstance.getId());
                myTask.setUserId(historicTaskInstance.getAssignee());
                myTask.setProcessDefId(historicTaskInstance.getProcessDefinitionId());
                myTask.setTaskDefinitionKey(historicTaskInstance.getTaskDefinitionKey());
                List<Comment> comments = taskService.getTaskComments(historicTaskInstance.getId());
                if (CollectionUtil.isNotEmpty(comments)) {
                    myTask.setComment(comments.get(0).getFullMessage());
                }
                if (ObjectUtil.isNotNull(historicTaskInstance.getAssignee())) {
                    myTask.setUserName(myCache.getUserName(historicTaskInstance.getAssignee()));
                }
                myTaskList.add(myTask);
            });
        }
        compareTask(myTaskList, true);
        data.put("data", myTaskList);
        data.put("page", page);
        result.setData(data);
        return result;
    }

    /**
     * 获取运行中的流程实例数据
     *
     * @param page
     * @return
     */
    public PageInfo getRunningProcessInstance(Page page) {
        PageHelper.startPage(page.getPageCurrent(), page.getPageSize());
        DangerInspectPublic dangerInspectPublic = new DangerInspectPublic();
        dangerInspectPublic.setFinish(false);
        List<DangerInspectPublic> dangerInspectPublics =
                dangerInspectPublicMapper.searchSimple(dangerInspectPublic, null);
        PageInfo pageInfo = new PageInfo<>(dangerInspectPublics);

        List<DangerProcess> dangerProcesses = compareRunningProcess(dangerInspectPublics);
        pageInfo.setList(dangerProcesses);
        return pageInfo;
    }

    private List<DangerProcess> compareRunningProcess(List<DangerInspectPublic> dangerInspectPublics) {
        if (CollectionUtil.isEmpty(dangerInspectPublics)) {
            return new ArrayList<>();
        }
        Map<String, DangerInspectPublic> instanceIdMap = dangerInspectPublics
                .parallelStream()
                .collect(Collectors
                        .toMap(DangerInspectPublic::getProcessInstanceId
                                , dangerInspectPublic1 -> dangerInspectPublic1));

        Set<String> processInstanceIds =
                dangerInspectPublics.stream()
                        .map(DangerInspectPublic::getProcessInstanceId)
                        .collect(Collectors.toSet());

        List<ProcessInstance> processInstances =
                runtimeservice.createProcessInstanceQuery()
                        .processInstanceIds(processInstanceIds).list();

        if (CollectionUtil.isEmpty(processInstances)) {
            return new ArrayList<>();
        }

        List<String> pIds = new ArrayList<>(processInstanceIds);
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceIdIn(pIds).list();
        List<DangerProcess> dangerProcesses = compareProcess(processInstances, instanceIdMap, tasks);
        return dangerProcesses;
    }

    public List<DangerProcess> getHistoryprocessInstance(Page page) {
        DangerInspectPublic dangerInspectPublic = new DangerInspectPublic();
        dangerInspectPublic.setFinish(true);
        List<DangerInspectPublic> dangerInspectPublics =
                dangerInspectPublicMapper.select(dangerInspectPublic);
        if (CollectionUtil.isEmpty(dangerInspectPublics)) {
            return new ArrayList<>();
        }
        Map<String, DangerInspectPublic> instanceIdMap = dangerInspectPublics
                .parallelStream()
                .collect(Collectors
                        .toMap(DangerInspectPublic::getProcessInstanceId
                                , dangerInspectPublic1 -> dangerInspectPublic1));
        Set<String> processInstanceIds =
                dangerInspectPublics.stream()
                        .map(DangerInspectPublic::getProcessInstanceId)
                        .collect(Collectors.toSet());
        List<HistoricProcessInstance> historicProcessInstances =
                historyService.createHistoricProcessInstanceQuery()
                        .processInstanceIds(processInstanceIds).list();
        if (CollectionUtil.isEmpty(historicProcessInstances)) {
            return new ArrayList<>();
        }

        return new ArrayList<>();
    }

    private void compareTask(List<MyTask> myTaskList, boolean history) {
        long start = System.currentTimeMillis();
        if (CollectionUtil.isEmpty(myTaskList)) {
            return;
        }
        myTaskList.forEach(myTask -> {
            String proceDefId = "";
            String bussKey = "";
            if (history) {
                HistoricProcessInstance historicProcessInstance =
                        historyService.createHistoricProcessInstanceQuery()
                                .processInstanceId(myTask.getProcessInstanceId())
                                .singleResult();
                proceDefId = historicProcessInstance.getProcessDefinitionId();
                bussKey = historicProcessInstance.getBusinessKey();
            } else {
                ProcessInstance processInstance = runtimeservice
                        .createProcessInstanceQuery()
                        .processInstanceId(myTask.getProcessInstanceId())
                        .singleResult();
                proceDefId = processInstance.getProcessDefinitionId();
                bussKey = processInstance.getBusinessKey();
            }

            ExtActNode extActNode = extActNodeMapper
                    .findByProcdefIdAndNodeId(proceDefId
                            , myTask.getTaskDefinitionKey());
            myTask.setExtActNode(extActNode);
            DangerInspectPublic dangerInspectPublic = new DangerInspectPublic();
            dangerInspectPublic.setId(Integer.parseInt(bussKey));
            List<DangerInspectPublic> dangerInspectPublics = dangerInspectPublicMapper.search(dangerInspectPublic, null);
            if (CollectionUtil.isNotEmpty(dangerInspectPublics)) {
                dangerInspectPublic = dangerInspectPublics.get(0);
                dangerInspectPublic.setStatus(extActNode.getStatus());
                dangerInspectPublic.setStatusName(extActNode.getName());
                if (dangerInspectListPublicMapper.findReformOverTime(new Date(), dangerInspectPublic.getId()) > 0) {
                    dangerInspectPublic.setOverTimeFlag(true);
                }
                if (dangerInspectListPublicMapper.findSuperviseOverTime(new Date()
                        , dangerInspectPublic.getId()) > 0) {
                    dangerInspectPublic.setOverTimeFlag(true);
                }
                myTask.setDangerInspectPublic(dangerInspectPublic);
            }
            ExtActTaskReassign extActTaskReassign =
                    extActTaskReassignService.findLatestReassign(myTask.getId());
            if (ObjectUtil.isNotNull(extActTaskReassign)) {
                myTask.setHasReassign(true);
                myTask.setExtActTaskReassign(extActTaskReassign);
            }
            SecurityUtils.getSecurityManager();

        });
    }


    private List<DangerProcess> compareProcess(List<ProcessInstance> processInstances
            , Map<String, DangerInspectPublic> instanceInspectMap
            , List<Task> tasks) {
        Map<String, List<Task>> taskMap = new HashMap<>();//key processInstanceId
        if (CollectionUtil.isNotEmpty(tasks)) {
            tasks.stream().forEach(task -> {

                if (taskMap.containsKey(task.getProcessInstanceId())) {
                    taskMap.get(task.getProcessInstanceId()).add(task);
                } else {
                    List<Task> taskList = new ArrayList<>();
                    taskList.add(task);
                    taskMap.put(task.getProcessInstanceId(), taskList);
                }
            });
        }
        List<DangerProcess> dangerProcesses = new ArrayList<>();
        processInstances.parallelStream().forEach(processInstance -> {
            DangerProcess dangerProcess = new DangerProcess();
            dangerProcess.setProcessInstanceid(processInstance.getProcessInstanceId());
            dangerProcess.setStartTime(processInstance.getStartTime());
            dangerProcess.setStartUserId(processInstance.getStartUserId());
            dangerProcess.setUsername(myCache.getUserName(processInstance.getStartUserId()));
            dangerProcess.setDangerInspectPublic(instanceInspectMap
                    .get(processInstance.getProcessInstanceId()));
            dangerProcess.getMyTasks().addAll(
                    transformTask(taskMap.get(processInstance.getProcessInstanceId())));
            dangerProcesses.add(dangerProcess);
        });

        return dangerProcesses;
    }

    private List<MyTask> transformTask(List<Task> tasks) {
        List<MyTask> myTaskList = new ArrayList<>();
        if (CollectionUtil.isEmpty(tasks)) {
            return myTaskList;
        }
        tasks.parallelStream().forEach(task -> {
            task.getExecutionId();
            MyTask myTask = new MyTask();
            myTask.setCreateTime(task.getCreateTime());
            myTask.setProcessInstanceId(task.getProcessInstanceId());
            myTask.setName(task.getName());
            myTask.setId(task.getId());
            myTask.setTaskDefinitionKey(task.getTaskDefinitionKey());
            myTask.setExecutionId(task.getExecutionId());
            myTask.setUserId(task.getAssignee());
            myTask.setUserName(myCache.getUserName(task.getAssignee()));
            if (ObjectUtil.isNotNull(task.getAssignee())) {
                myTask.setUserName(myCache.getUserName(task.getAssignee()));
            }
            myTaskList.add(myTask);
        });
        return myTaskList;
    }
}
